package com.ruoyi.framework.config;


import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.opslab.util.encrypt.Base64;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.redis.shiro.RedisCacheManager;
import com.ruoyi.framework.redis.shiro.RedisManager;
import com.ruoyi.framework.redis.shiro.RedisSessionDAO;
import com.ruoyi.framework.shiro.realm.UserRealm;
import com.ruoyi.framework.shiro.web.filter.LogoutFilter;
import com.ruoyi.framework.shiro.web.filter.captcha.CaptchaValidateFilter;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.eis.MemorySessionDAO;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 权限配置加载
 *
 * @author ruoyi
 */
@Configuration
public class ShiroConfig {
  public static final String PREMISSION_STRING = "perms[\"{0}\"]";

  // Session超时时间，单位为毫秒（默认30分钟）
  @Value("${shiro.session.expireTime}")
  private int expireTime;

  // 相隔多久检查一次session的有效性，单位毫秒，默认就是10分钟
  @Value("${shiro.session.validationInterval}")
  private int validationInterval;

  // 验证码开关
  @Value("${shiro.user.captchaEbabled}")
  private boolean captchaEbabled;

  // 验证码类型
  @Value("${shiro.user.captchaType}")
  private String captchaType;

  // 设置Cookie的域名
  @Value("${shiro.cookie.domain}")
  private String domain;

  // 设置cookie的有效访问路径
  @Value("${shiro.cookie.path}")
  private String path;

  // 设置HttpOnly属性
  @Value("${shiro.cookie.httpOnly}")
  private boolean httpOnly;

  // 设置Cookie的过期时间，秒为单位
  @Value("${shiro.cookie.maxAge}")
  private int maxAge;

  // 登录地址
  @Value("${shiro.user.loginUrl}")
  private String loginUrl;

  // 权限认证失败地址
  @Value("${shiro.user.unauthorizedUrl}")
  private String unauthorizedUrl;

  @Value("${spring.redis.host}")
  private String host;

  @Value("${spring.redis.port}")
  private int port;

  @Value("${spring.redis.timeout}")
  private int timeout;

  @Value("${cacheType}")
  private String cacheType;

  @Value("${server.session.timeout}")
  private int tomcatTimeout;

  @Bean
  public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
    return new LifecycleBeanPostProcessor();
  }


  /**
   * Shiro过滤器配置
   */
  @Bean
  public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    // Shiro的核心安全接口,这个属性是必须的
    shiroFilterFactoryBean.setSecurityManager(securityManager);
    // 身份认证失败，则跳转到登录页面的配置
    shiroFilterFactoryBean.setLoginUrl(loginUrl);
    // 权限认证失败，则跳转到指定页面
    shiroFilterFactoryBean.setUnauthorizedUrl(unauthorizedUrl);
    // Shiro连接约束配置，即过滤链的定义
    LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
    // 对静态资源设置匿名访问
    filterChainDefinitionMap.put("/favicon.ico**", "anon");
    filterChainDefinitionMap.put("/ruoyi.png**", "anon");
    filterChainDefinitionMap.put("/css/**", "anon");
    //API接口
    filterChainDefinitionMap.put("/api/v1/**", "anon");

    // swagger接口文档
    filterChainDefinitionMap.put("/v2/api-docs", "anon");
    filterChainDefinitionMap.put("/webjars/**", "anon");
    filterChainDefinitionMap.put("/swagger-resources/**", "anon");
    filterChainDefinitionMap.put("/swagger-ui.html", "anon");
    filterChainDefinitionMap.put("/docs/**", "anon");
    filterChainDefinitionMap.put("/fonts/**", "anon");
    filterChainDefinitionMap.put("/img/**", "anon");
    filterChainDefinitionMap.put("/ajax/**", "anon");
    filterChainDefinitionMap.put("/js/**", "anon");
    filterChainDefinitionMap.put("/ruoyi/**", "anon");
    filterChainDefinitionMap.put("/druid/**", "anon");
    filterChainDefinitionMap.put("/captcha/captchaImage**", "anon");
    // 退出 logout地址，shiro去清除session
    filterChainDefinitionMap.put("/logout", "logout");
    // 不需要拦截的访问
    filterChainDefinitionMap.put("/login", "anon,captchaValidate");

    Map<String, Filter> filters = new LinkedHashMap<>();
    filters.put("captchaValidate", captchaValidateFilter());
    filters.put("logout", logoutFilter());
    shiroFilterFactoryBean.setFilters(filters);

    // 所有请求需要认证
    filterChainDefinitionMap.put("/**", "user");
    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

    return shiroFilterFactoryBean;
  }

  /**
   * 缓存管理器 使用Ehcache实现
   */
  @Bean
  public EhCacheManager ehCacheManager() {
    net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("ruoyi");
    EhCacheManager em = new EhCacheManager();
    if(StringUtils.isNull(cacheManager)) {
      em.setCacheManagerConfigFile("classpath:ehcache/ehcache-shiro.xml");
      return em;
    } else {
      em.setCacheManager(cacheManager);
      return em;
    }
  }

  /**
   * 缓存管理器 redis实现
   * 使用的是shiro-redis开源插件
   *
   * @return
   */
  public RedisCacheManager cacheManager() {
    RedisCacheManager redisCacheManager = new RedisCacheManager();
    redisCacheManager.setRedisManager(redisManager());
    return redisCacheManager;
  }
  


  /**
   * 配置shiro redisManager
   * 使用的是shiro-redis开源插件
   *
   * @return
   */
  public RedisManager redisManager() {
    RedisManager redisManager = new RedisManager();
    redisManager.setHost(host);
    redisManager.setPort(port);
    redisManager.setExpire(1800);// 配置缓存过期时间
    redisManager.setTimeout(timeout);
    // redisManager.setPassword(password);
    return redisManager;
  }

  /**
   * RedisSessionDAO shiro sessionDao层的实现 通过redis
   * 使用的是shiro-redis开源插件
   */
  @Bean
  public RedisSessionDAO redisSessionDAO() {
    RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
    redisSessionDAO.setRedisManager(redisManager());
    return redisSessionDAO;
  }

  @Bean
  public SessionDAO sessionDAO() {
    if(Constants.CACHE_TYPE_REDIS.equals(cacheType)) {
      return redisSessionDAO();
    } else {
      return new MemorySessionDAO();
    }
  }

  /**
   * shiro session会话管理器
   */
  @Bean
  public DefaultWebSessionManager sessionManager() {
    //DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    //sessionManager.setSessionDAO(redisSessionDAO());
    //return sessionManager;
    DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    sessionManager.setGlobalSessionTimeout(tomcatTimeout * 1000);
    sessionManager.setSessionDAO(sessionDAO());
    Collection<SessionListener> listeners = new ArrayList<SessionListener>();
    listeners.add(new BDSessionListener());
    sessionManager.setSessionListeners(listeners);
    return sessionManager;
  }
  /**
   * 安全管理器
   */
  @Bean
  public SecurityManager securityManager() {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    // 设置realm.
    securityManager.setRealm(userRealm());
    // 记住我
    securityManager.setRememberMeManager(rememberMeManager());
    // 注入缓存管理器;
    // 自定义缓存实现 使用redis
    if(Constants.CACHE_TYPE_REDIS.equals(cacheType)) {
      securityManager.setCacheManager(cacheManager());
    } else {
      securityManager.setCacheManager(ehCacheManager());
    }
    // session管理器
    securityManager.setSessionManager(sessionManager());
    return securityManager;
  }
  @Bean
  public UserRealm userRealm(){
    UserRealm userRealm = new UserRealm();
    return userRealm;
  }

  /**
   * 退出过滤器
   */
  public LogoutFilter logoutFilter() {
    LogoutFilter logoutFilter = new LogoutFilter();
    logoutFilter.setLoginUrl(loginUrl);
    return logoutFilter;
  }
  

  /**
   * 自定义验证码过滤器
   */
  @Bean
  public CaptchaValidateFilter captchaValidateFilter() {
    CaptchaValidateFilter captchaValidateFilter = new CaptchaValidateFilter();
    captchaValidateFilter.setCaptchaEbabled(captchaEbabled);
    captchaValidateFilter.setCaptchaType(captchaType);
    return captchaValidateFilter;
  }

  /**
   * cookie 属性设置
   */
  public SimpleCookie rememberMeCookie() {
    SimpleCookie cookie = new SimpleCookie("rememberMe");
    cookie.setDomain(domain);
    cookie.setPath(path);
    cookie.setHttpOnly(httpOnly);
    cookie.setMaxAge(maxAge * 24 * 60 * 60);
    return cookie;
  }

  /**
   * 记住我
   */
  public CookieRememberMeManager rememberMeManager() {
    CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
    cookieRememberMeManager.setCookie(rememberMeCookie());
    cookieRememberMeManager.setCipherKey(Base64.decode("fCq+/xW488hMTCD+cmJ3aQ=="));
    return cookieRememberMeManager;
  }

  /**
   * 开启Shiro代理
   */
  @Bean
  public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
    DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
    proxyCreator.setProxyTargetClass(true);
    return proxyCreator;
  }

  /**
   * thymeleaf模板引擎和shiro框架的整合
   */
  @Bean
  public ShiroDialect shiroDialect() {
    return new ShiroDialect();
  }

  /**
   * 开启Shiro注解通知器
   */
  @Bean
  public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
      @Qualifier("securityManager") SecurityManager securityManager) {
    AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
    authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
    return authorizationAttributeSourceAdvisor;
  }
}
